/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package grex;

import grex.fitnessfunctions.ErrorFunctions.ErrorCalculator;
import grex.fitnessfunctions.ErrorFunctions.IErrorFunction;
import grex.genes.GeneException;

import java.io.Serializable;
import java.text.NumberFormat;
import java.util.ArrayList;

/**
 *
 * @author RIK
 */
public class EvolutionResult implements Serializable {

    private double avgTrainACC = 0;
    private double avgTrainAUC = 0;
    private double stdTrainACC = 0;
    private double stdTrainAUC = 0;
    private double avgTestACC = 0;
    private double avgTestAUC = 0;
    private double avgTestRMSE = 0;
    private double avgTestMUAPE = 0;
    private double avgTrainRMSE = 0;
    private double avgTrainMUAPE = 0;
    private double stdTestACC = 0;
    private double stdTestAUC = 0;
    private double avgTrainBrier = 0;
    private double stdTrainBrier = 0;
    private double avgTestBrier = 0;
    private double stdTestBrier = 0;
    private double avgLength = 0;
    private double stdLength = 0;
    private double avgFitness = 0;
    private double stdFitness = 0;
    private double ensTestACC = 0;
    private double ensTrainACC = 0;
    private double ensTestAUC = 0;
    private double ensTrainAUC = 0;
    private double ensTestBrier = 0;
    private double ensTrainBrier = 0;
    private double ensAVGTestAUC = 0;
    private double ensAVGTrainAUC = 0;
    private double ensTrainRMSE = 0;
    private double ensTrainMUAPE = 0;
    private double ensTrainGMRAE = 0;
    private double ensTestRMSE = 0;
    private double ensTestMUAPE = 0;
    private double ensTestGMRAE = 0;



    private boolean classification = true;
    private ArrayList<Prediction> ensTrains,  ensTests;
    private GP bestGP;
    private ArrayList<GP> batchGPs;
    private Options options;
    ErrorCalculator ec;
    public EvolutionResult(ArrayList<GP> batchGPs, Options options) {
        ec = new ErrorCalculator(options.getPROBLEM_TYPE());
        this.batchGPs = batchGPs;
        this.options = options;
        classification = batchGPs.get(0).getOptions().getPROBLEM_TYPE() == Options.CLASSIFICATION;
        for (GP gp : batchGPs) {
            gp.setOptions(options);
        }
        bestGP = getBestGpFromBatch();
            calcBatchStatistics();
            ensTests = calcBatchTestEnsStatistics();
            ensTrains = calcBatchTrainEnsStatistics();
       
    }

    public String toString() {
        try {
            String s = "";
            NumberFormat nf = NumberFormat.getInstance();
            nf.setMaximumFractionDigits(4);
            s += "\nAverage Fitness:\t" + nf.format(getAvgFitness()) + "\t std: \t" + nf.format(getStdFitness()) + "\n";
            s += "Average Length:\t\t" + nf.format(getAvgLength()) + "\t std: \t" + nf.format(getStdLength()) + "\n";
            s += "\n";
            if (classification) {
                s += "Average Train ACC:\t" + nf.format(getAvgTrainACC()) + "\t std: \t" + nf.format(getStdTrainACC()) + "\n";
                s += "Average Train AUC:\t" + nf.format(getAvgTrainAUC()) + "\t std: \t" + nf.format(getStdTrainAUC()) + "\n";
                s += "Average Train BRI:\t" + nf.format(getAvgTrainBrier()) + "\t std: \t" + nf.format(getStdTrainBrier()) + "\n";

                s += "\n";
                s += "Average Test ACC:\t" + nf.format(getAvgTestACC()) + "\t std: \t" + nf.format(getStdTestACC()) + "\n";
                s += "Average Test AUC:\t" + nf.format(getAvgTestAUC()) + "\t std: \t" + nf.format(getStdTestAUC()) + "\n";
                s += "Average Test BRI:\t" + nf.format(getAvgTestBrier()) + "\t std: \t" + nf.format(getStdTestBrier()) + "\n";
            } else {
                s += "Average Train RMSE:\t" + nf.format(avgTrainRMSE)+ "\n";
                s += "Average Train MUAPE:\t" + nf.format(avgTrainMUAPE)  + "\n";
            }
            s += "\n";
            s += "Best GP in batch:" + (getBatchGPs().indexOf(bestGP) + 1) + "\n";
            s += bestGP.getStatisticsAsString();
            s += bestGP.getFormatedTree();
            s += "\n\n";
            s += options.toString() + "\n\n";
            s += "<-----------Test predictions----------->\n";
            s += bestGP.getGPTestPrediction();
            s += "<-----------Train predictions----------->\n";
            s += bestGP.getGPTrainPrediction();
            
            return s;
        } catch (Exception ex) {
            ErrorManager.getInstance().reportError(ex);
        }
        return "Error";
    }

    public GP getBestGpFromBatch() {
        GP best;
        best = null;
        for (GP current : getBatchGPs()) {
            if (best == null || best.getFitness() > current.getFitness()) {
                best = current;
            }
        }
        return best;
    }

    public ArrayList<Prediction> calcBatchTrainEnsStatistics() {
        ArrayList<Prediction> orderedTrains=batchGPs.get(0).getOrderedTrain();

        try {

            getBatchGPs().get(0).execute(getBatchGPs().get(0).getPcTrain());
            PredictionContainer majTrain = getBatchGPs().get(0).getPcTrain().clone();
            ArrayList<Prediction> allTrain = new ArrayList<Prediction>();
            double[] support = new double[batchGPs.get(0).getEnvironment().getNrOfTargetCategories()];
            ArrayList<PredictionContainer> trainPCs = new ArrayList<PredictionContainer>();
            
            for (GP gp : getBatchGPs()) {

                gp.execute(gp.getPcTrain());
                trainPCs.add(gp.getPcTrain());
            }            
            for (Prediction guide : orderedTrains) {//trainPCs.get(0).values()) {
                //Sum votes
                support = new double[support.length];
                double[] s;
                double pred = 0;
                Prediction p = null;
                for (PredictionContainer pc : trainPCs) {
                    p = pc.get(guide.getInstance());
                    if (classification) {
                        s = p.getProbs();
                        for (int j = 0; j < support.length; j++) {
                            support[j] += s[j] / getBatchGPs().size();
                        }
                    } else {
                        pred += p.getPrediction() / getBatchGPs().size();
                    }
                }
                Prediction x = majTrain.get(guide.getInstance());
                if (classification) {
                    x.setSupport(support);
                } else {
                    x.setPrediction(pred);
                }
                allTrain.add(x);
            }
            //TEST**********************************           
            NumberFormat nf = NumberFormat.getInstance();
            nf.setMaximumFractionDigits(4);
            Environment e = getBatchGPs().get(0).getEnvironment();
            ensTrainACC = ec.calcError(majTrain,"ACC");// sacc.calcACC(getBatchGPs().get(0).getOptions(), majTrain);
            ensTrainAUC = ec.calcError( majTrain,"AUC");//sauc.calcAUC(getBatchGPs().get(0).getEnvironment(), majTrain);
            ensTrainBrier = ec.calcError( majTrain,"BRI");//sbri.calcBRIER(majTrain);
            ensAVGTrainAUC = ec.calcError( allTrain,"AUC");//sauc.calcAUC(getBatchGPs().get(0).getEnvironment(), allTrain);

            ensTrainMUAPE =  ec.calcError(majTrain,IErrorFunction.MAPE);
            ensTrainRMSE = ec.calcError( majTrain, IErrorFunction.RMSE);// rmse.calcRMSE(majTrain);
            return allTrain;
        } catch (GeneException ex) {
            ErrorManager.getInstance().reportError(ex);
            return null;
        }
    }

    public ArrayList<Prediction> calcBatchTestEnsStatistics() {
    ArrayList<Prediction> orderedTests=batchGPs.get(0).getOrderedTest();
        try {
            System.out.println("Calculating Ensemble Statistics..");
            getBatchGPs().get(0).execute(getBatchGPs().get(0).getPcTest());
            PredictionContainer majTest = getBatchGPs().get(0).getPcTest().clone();
            PredictionContainer all = new PredictionContainer(majTest.size());
            ArrayList<Prediction> allTest = new ArrayList<Prediction>();
            double[] support = new double[batchGPs.get(0).getEnvironment().getNrOfTargetCategories()];
            ArrayList<PredictionContainer> testPCs = new ArrayList<PredictionContainer>();
            for (GP gp : getBatchGPs()) {
                gp.execute(gp.getPcTest());
                testPCs.add(gp.getPcTest());
            }
            //TEST**********************************
            for (Prediction guide : orderedTests){//testPCs.get(0).values()) {
                //Sum votes
                support = new double[support.length];
                double[] s;
                Prediction p = null;
                double pred = 0;
                for (PredictionContainer pc : testPCs) {
                    p = pc.get(guide.getInstance());
                    if (classification) {
                        s = p.getProbs();
                        for (int j = 0; j < support.length; j++) {
                            support[j] += s[j] / getBatchGPs().size();
                        }
                    } else {
                        pred += p.getPrediction() / getBatchGPs().size();
                    }
                }

                Prediction x = majTest.get(guide.getInstance());
                if (classification) {
                    x.setSupport(support);
                } else {
                    x.setPrediction(pred);
                }
                allTest.add(x);
            }
            NumberFormat nf = NumberFormat.getInstance();
            nf.setMaximumFractionDigits(4);
            Environment e = getBatchGPs().get(0).getEnvironment();
            ensTestACC = ec.calcError( majTest, "ACC");// sacc.calcACC(getBatchGPs().get(0).getOptions(), majTest);
            ensTestAUC = ec.calcError( majTest, "AUC");//sauc.calcAUC(getBatchGPs().get(0).getEnvironment(), majTest);
            ensTestBrier = ec.calcError( majTest, "BRI");//sbri.calcBRIER(majTest);

            
            ensAVGTestAUC = ec.calcError( allTest, "AUC");//sauc.calcAUC(getBatchGPs().get(0).getEnvironment(), allTest);
            ensTestRMSE = ec.calcError( ensTrains, "RMSE");//.calcRMSE(majTest);
            ensTestMUAPE = ec.calcError( ensTrains, "MAPE");//.calcRMSE(majTest);
            return allTest;
        } catch (GeneException ex) {
            ErrorManager.getInstance().reportError(ex);
            return null;
        }
    }

    private void calcBatchStatistics() {
        try {

            int n = getBatchGPs().size();
            avgTrainACC = 0;
            avgTrainAUC = 0;
            stdTrainACC = 0;
            stdTrainAUC = 0;
            avgTestACC = 0;
            avgTestAUC = 0;
            stdTestACC = 0;
            stdTestAUC = 0;
            avgLength = 0;
            stdLength = 0;
            avgFitness = 0;
            stdFitness = 0;
            stdTestBrier = 0;
            stdTrainBrier = 0;
            avgTrainBrier = 0;
            avgTestBrier = 0;
            avgTestRMSE = 0;
            avgTestMUAPE = 0;
            avgTrainRMSE = 0;
            avgTrainRMSE = 0;
            ArrayList<Prediction> aTrain = new ArrayList<Prediction>();
            ArrayList<Prediction> aTest = new ArrayList<Prediction>();
            
            for (GP gp : getBatchGPs()) {
                gp.execute(gp.getPcTest());
                gp.execute(gp.getPcTrain());
                for (Prediction p : gp.getPcTrain().values()) {
                    aTrain.add(p);
                }
                for (Prediction p : gp.getPcTest().values()) {
                    aTest.add(p);
                }
            }
            avgTrainAUC += ec.calcError(aTrain,"AUC");
            avgTestAUC += ec.calcError(aTest,"AUC");
            for (GP gp : getBatchGPs()) {
                avgTestACC += gp.getTestAccError() / n;
                //         avgTestAUC += gp.getTestAUC() / n;
                avgTestBrier += gp.getTestBRIERError() / n;
                avgTrainACC += gp.getTrainAccError() / n;
                //         avgTrainAUC += gp.getTrainAUC() / n;
                avgTrainBrier += gp.getTrainBRIERError() / n;
                avgLength += (1.0 / n) * gp.getNrOfNodes();
                avgFitness += gp.getFitness() / n;
                avgTrainRMSE += gp.calcTrainError("RMSE")/n;//, null)rmse.calcRMSE(gp.getPcTrain()) / n;
                avgTestRMSE += gp.calcTestError("RMSE")/n;//rmse.calcRMSE(gp.getPcTest()) / n;
                avgTrainMUAPE += gp.calcTrainError("MAPE")/n;//muape.calcMUAPE(gp, gp.getPcTrain()) / n;
                avgTestMUAPE += gp.calcTestError("MAPE")/n;//muape.calcMUAPE(gp, gp.getPcTest()) / n;
            }


            for (GP gp : getBatchGPs()) {
                gp.setOptions(options);
                stdTestACC += Math.pow(gp.getTestAccError() - avgTestACC, 2) / n;
                stdTestAUC += Math.pow(gp.getTestAUCError() - avgTestAUC, 2) / n;
                stdTestBrier += Math.pow(gp.getTestBRIERError() - getAvgTestBrier(), 2) / n;
                stdTrainACC += Math.pow(gp.getTrainAccError() - avgTrainACC, 2) / n;
                stdTrainAUC += Math.pow(gp.getTrainAUCError() - avgTrainAUC, 2) / n;
                stdTrainBrier += Math.pow(gp.getTrainBRIERError() - getAvgTrainBrier(), 2) / n;
                stdLength += Math.pow(gp.getNrOfNodes() - avgLength, 2) / n;
                stdFitness += Math.pow(gp.getFitness() - avgFitness, 2) / n;
            }
            stdTestACC = Math.sqrt(stdTestACC);
            stdTestAUC = Math.sqrt(stdTestAUC);
            stdTrainACC = Math.sqrt(stdTrainACC);
            stdTrainAUC = Math.sqrt(stdTrainAUC);
            stdTestBrier = Math.sqrt(getStdTestBrier());
            stdTrainBrier = Math.sqrt(getStdTrainBrier());
            stdLength = Math.sqrt(stdLength);
            stdFitness = Math.sqrt(stdFitness);


        } catch (GeneException e) {
            ErrorManager.getInstance().reportError(e);
        }
    }



    public double getAvgTrainACC() {
        return avgTrainACC;
    }

    public void setAvgTrainACC(double avgTrainACC) {
        this.avgTrainACC = avgTrainACC;
    }

    public double getAvgTrainAUC() {
        return avgTrainAUC;
    }

    public void setAvgTrainAUC(double avgTrainAUC) {
        this.avgTrainAUC = avgTrainAUC;
    }

    public double getStdTrainACC() {
        return stdTrainACC;
    }

    public void setStdTrainACC(double stdTrainACC) {
        this.stdTrainACC = stdTrainACC;
    }

    public double getStdTrainAUC() {
        return stdTrainAUC;
    }

    public void setStdTrainAUC(double stdTrainAUC) {
        this.stdTrainAUC = stdTrainAUC;
    }

    public double getAvgTestACC() {
        return avgTestACC;
    }

    public void setAvgTestACC(double avgTestACC) {
        this.avgTestACC = avgTestACC;
    }

    public double getAvgTestAUC() {
        return avgTestAUC;
    }

    public void setAvgTestAUC(double avgTestAUC) {
        this.avgTestAUC = avgTestAUC;
    }

    public double getStdTestACC() {
        return stdTestACC;
    }

    public void setStdTestACC(double stdTestACC) {
        this.stdTestACC = stdTestACC;
    }

    public double getStdTestAUC() {
        return stdTestAUC;
    }

    public void setStdTestAUC(double stdTestAUC) {
        this.stdTestAUC = stdTestAUC;
    }

    public double getAvgLength() {
        return avgLength;
    }

    public void setAvgLength(double avgLength) {
        this.avgLength = avgLength;
    }

    public double getStdLength() {
        return stdLength;
    }

    public void setStdLength(double stdLength) {
        this.stdLength = stdLength;
    }

    public double getAvgFitness() {
        return avgFitness;
    }

    public void setAvgFitness(double avgFitness) {
        this.avgFitness = avgFitness;
    }

    public double getStdFitness() {
        return stdFitness;
    }

    public void setStdFitness(double stdFitness) {
        this.stdFitness = stdFitness;
    }

    public GP getBestGP() {
        return bestGP;
    }

    public void setBestGP(GP bestGP) {
        this.bestGP = bestGP;
    }

    public double getAvgTrainBrier() {
        return avgTrainBrier;
    }

    public double getStdTrainBrier() {
        return stdTrainBrier;
    }

    public double getAvgTestBrier() {
        return avgTestBrier;
    }

    public double getStdTestBrier() {
        return stdTestBrier;
    }

    public double getEnsTestACC() {
        return ensTestACC;
    }

    public double getEnsTrainACC() {
        return ensTrainACC;
    }

    public double getEnsTestAUC() {
        return ensTestAUC;
    }

    public double getEnsTrainAUC() {
        return ensTrainAUC;
    }

    public double getEnsTestBrier() {
        return ensTestBrier;
    }

    public double getEnsTrainBrier() {
        return ensTrainBrier;
    }

    public double getEnsAVGTestAUC() {
        return ensAVGTestAUC;
    }

    public double getEnsAVGTrainAUC() {
        return ensAVGTrainAUC;
    }

    public ArrayList<Prediction> getEnsTrains() {
        return ensTrains;
    }

    public ArrayList<Prediction> getEnsTests() {
        return ensTests;
    }

    public double getAvgTestRMSE() {
        return avgTestRMSE;
    }

    public double getAvgTestMUAPE() {
        return avgTestMUAPE;
    }

    public double getAvgTrainRMSE() {
        return avgTrainRMSE;
    }

    public double getAvgTrainMUAPE() {
        return avgTrainMUAPE;
    }

    public double getEnsTrainRMSE() {
        return ensTrainRMSE;
    }

    public double getEnsTrainMUAPE() {
        return ensTrainMUAPE;
    }

    public double getEnsTestRMSE() {
        return ensTestRMSE;
    }

    public double getEnsTestMUAPE() {
        return ensTestMUAPE;
    }

    public ArrayList<GP> getBatchGPs() {
        return batchGPs;
    }
}
